﻿using System;
using System.Collections.Generic;
using ZyGame.Execute;
using System.IO;
using UnityEngine;
using Object = UnityEngine.Object;
using System.Collections;

namespace ZyGame.Resource
{
    class ResourceManager : Singleton<ResourceManager>
    {
        private bool EnsureAssetPathStatus(string url)
        {
            if (url.IsNullOrEmpty())
            {
                Client.Console.WriteError(new ArgumentNullException("assetName"));
                return false;
            }
            if (Path.GetExtension(url).IsNullOrEmpty())
            {
                Client.Console.WriteError(new Exception("加载文件需要文件扩展名：" + url));
                return false;
            }
            return true;
        }


        private static bool EnsureInternalOrEditorLoadAsset(string url)
        {
            if (url.StartsWith("Resources"))
            {
                return true;
            }
#if UNITY_EDITOR
            if (AppConfig.HasUseAssetsHotfix() is false)
            {
                return true;
            }
#endif
            return false;
        }

        public SceneLoadRunnable LoadSceneAsync(string name)
        {
            return SceneLoadRunnable.Create(name);
        }

        /// <summary>
        /// 加载资源
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public T LoadAsset<T>(string url, GameObject bind = null) where T : Object
        {
            if (EnsureAssetPathStatus(url) is false)
            {
                return default;
            }
            if (url.StartsWith("http"))
            {
                Client.Console.WriteLine(new Exception("网络资源不能同步加载"));
                return default;
            }
            BundleRefrence context = default;
            Client.Console.WriteLine("[LOAD ASSETS]" + url);
            T result = default(T);
            if (EnsureInternalOrEditorLoadAsset(url))
            {
                if (!RefrenceManager.instance.TryOutBundle("EDITOR", out context))
                {
                    RefrenceManager.instance.Enqueue(context = new BundleRefrence("EDITOR"));
                }
                return context.GetObject<T>(url, bind);
            }
            PackageInfo package = PackageInfo.GetPackageWithAssetName(url);
            if (package is not null)
            {
                if (!RefrenceManager.instance.TryOutBundle(package.name, out context))
                {
                    context = LoadingManager.instance.LoadSync(package);
                }
                result = context.GetObject<T>(url, bind);
            }
            else
            {
                if (RefrenceManager.instance.TryOutBundleWithAssetName(url, out context))
                {
                    result = context.GetObject<T>(url, bind);
                }
            }
            return result;
        }

        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        internal Runnable<T> LoadAssetAsync<T>(string url, GameObject bindObject = null) where T : Object
        {
            if (EnsureAssetPathStatus(url) is false)
            {
                return Client.Running.FromException<T>(new Exception("加载资源失败，详情请看控制台输出！"));
            }
            Client.Console.WriteLine("[LOAD ASSETS]" + url);
            if (Client.Running.TryGetOrCreate(url, out Runnable<T> runnable))
            {
                return runnable;
            }
            BundleRefrence context = default;
            if (EnsureInternalOrEditorLoadAsset(url))
            {
                if (!RefrenceManager.instance.TryOutBundle("EDITOR", out context))
                {
                    RefrenceManager.instance.Enqueue(context = new BundleRefrence("EDITOR"));
                }
                runnable.Execute(WaitingLoadAssetObject);
                return runnable;
            }
            PackageInfo package = PackageInfo.GetPackageWithAssetName(url);
            if (package is null)
            {
                if (!RefrenceManager.instance.TryOutBundleWithAssetName(url, out context))
                {
                    runnable.Execute(WaitingLoadAssetObject);
                }
                return runnable;
            }
            if (RefrenceManager.instance.TryOutBundle(package.name, out context))
            {
                runnable.Execute(WaitingLoadAssetObject);
                return runnable;
            }
            IEnumerator WaitingLoadAssetObject()
            {
                yield return context.GetObjectAsync(url, runnable, bindObject);
            }

            IEnumerator WaitingLoadingAssetbundle()
            {
                var waiting = LoadingManager.instance.LoadAsync(package);
                yield return waiting.Waitting();
                if (waiting.EnsureExecutedSuccessful() is false)
                {
                    yield break;
                }
                context = (BundleRefrence)waiting.result;
            }
            runnable.Execute(WaitingLoadingAssetbundle)
                    .Execute(WaitingLoadAssetObject);
            return runnable;
        }

        internal Runnable<T> LoadNetworkAsync<T>(string url, string name = "", GameObject bindObject = null, uint version = 0, uint crc = 0) where T : Object
        {
            if (url.StartsWith("http") is false)
            {
                return LoadAssetAsync<T>(url, bindObject);
            }
            if (Client.Running.TryGetOrCreate(url, out Runnable<T> result))
            {
                return result;
            }
            string bundleName = Path.GetFileName(url);
            if (RefrenceManager.instance.TryOutBundle(bundleName, out BundleRefrence context))
            {
                result.Execute(WaitingLoadAssetObject);
                return result;
            }
            IEnumerator WaitingLoadAssetObject()
            {
                if (name.IsNullOrEmpty())
                {
                    name = Path.GetFileNameWithoutExtension(url);
                }
                yield return context.GetObjectAsync(name, result, bindObject);
            }

            IEnumerator WaitingLoadingAssetbundle()
            {
                var waiting = LoadingManager.instance.LoadNetAsync(url, version, crc);
                yield return waiting.Waitting();
                if (waiting.EnsureExecutedSuccessful() is false)
                {
                    yield break;
                }
                context = (BundleRefrence)waiting.result;
            }

            result.Execute(WaitingLoadingAssetbundle)
                  .Execute(WaitingLoadAssetObject);
            return result;
        }

        internal Runnable LoadNetworkBundleAsync(string url, uint version, uint crc)
        {
            string bundleName = Path.GetFileName(url);
            if (RefrenceManager.instance.TryOutBundle(bundleName, out BundleRefrence context))
            {
                return LoadingManager.Completed;
            }
            if (Client.Running.TryGetOrCreate(url, out Runnable runnable))
            {
                return runnable;
            }
            IEnumerator WaitingLoadingAssetbundle()
            {
                var waiting = LoadingManager.instance.LoadNetAsync(url, version, crc);
                yield return waiting.Waitting();
                if (waiting.EnsureExecutedSuccessful() is false)
                {
                    yield break;
                }
                context = (BundleRefrence)waiting.result;
            }
            runnable.Execute(WaitingLoadingAssetbundle);
            return runnable;
        }

        /// <summary>
        /// 回收资源
        /// </summary>
        /// <param name="context"></param>
        internal void Release(Object context)
        {
            if (context is null)
            {
                return;
            }
            Client.Console.WriteLine("[RELEASE ASSETS]:" + context.name);
            RefrenceManager.instance.Release(context);
        }

        internal CheckUpdateRunnable CheckUpdateAssetList(string module)
        {
#if UNITY_EDITOR
            if (!AppConfig.HasUseAssetsHotfix())
            {
                return CheckUpdateRunnable.NoUpdateRequired;
            }
#endif
            return CheckUpdateRunnable.Create(module);
        }

        public UpdateResourceRunnable UpdateModuleAssetList(List<PackageInfo> files)
        {
            return UpdateResourceRunnable.Create(files.ToArray());
        }

        internal LoadingBundleRunnable PreLoadBundle(string module)
        {
#if UNITY_EDITOR
            if (!AppConfig.HasUseAssetsHotfix())
            {
                return LoadingManager.Completed;
            }
#endif
            PackageInfo.LoadModulePackageConfig(module);
            return LoadingManager.instance.LoadMoudlePackages(module);
        }
    }
}